home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 039a / cdbw.zip / SCLIENT.C < prev    next >
Text File  |  1991-05-15  |  20KB  |  669 lines

  1. /*
  2.  *  SCLIENT.C
  3.  *
  4.  *  This module contains functions associated with the client dialog box
  5.  *  in SAMPLE.EXE.
  6.  *
  7.  *  Copyright (C) 1991 by Daytris.  All rights reserved.
  8.  */
  9.  
  10. #include <windows.h>
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <math.h>
  15. #ifndef ZORTECH
  16. #include <memory.h>
  17. #endif
  18. #include "dbmgr.h"
  19. #include "sampledb.h"
  20. #include "sample.h"
  21.  
  22.  
  23. /************************************************
  24.  * Local data
  25.  ************************************************/
  26.  
  27. static CLIENT client;
  28. static enum 
  29.     {
  30.     MODE_ADD,
  31.     MODE_UPDATE,
  32.     MODE_DELETE
  33.     } eMode;
  34. static BOOL bAddressChange;
  35. static HANDLE hAddresses;
  36.  
  37.  
  38. /************************************************
  39.  * Function Declarations
  40.  ************************************************/
  41.  
  42. BOOL AddClientDlg( HWND hWnd);
  43. BOOL UpdateClientDlg( HWND hWnd);
  44. BOOL DeleteClientDlg( HWND hWnd);
  45. static BOOL GetSelectedClient( HWND hWnd, CLIENT *pClient, short *pIndexSel);
  46. static BOOL AddClientAddresses( HWND hWnd);
  47. static BOOL DeleteClientAddresses( HWND hWnd);
  48. static BOOL FreeClientAddresses( HWND hWnd);
  49. BOOL FAR PASCAL ClientProc( HWND hDlg, unsigned iMessage, WORD wParam,
  50.     LONG lParam);
  51. static void SetClientFields( HWND hDlg);
  52. static BOOL GetClientFields( HWND hDlg);
  53. static BOOL StoreAddressHandles( HWND hDlg);
  54.  
  55.  
  56. /***************************************************************************
  57.  * Function : AddClientDlg
  58.  *
  59.  * Purpose  : This function drives the add client dialog box.
  60.  *
  61.  * Returns  : TRUE  - client added
  62.  *            FALSE - add aborted or error in add
  63.  ***************************************************************************/
  64. BOOL AddClientDlg( HANDLE hWnd)
  65.     {
  66.     short nStatus;
  67.     DWORD dwStatus;
  68.     FARPROC lpfnClientProc;
  69.  
  70.     /* Set static variables */
  71.     eMode = MODE_ADD;
  72.     bAddressChange = FALSE;
  73.  
  74.     /* Initialize the client structure */
  75.     memset( &client, 0, sizeof( CLIENT));
  76.     client.lClientNbr = setup.lNextClientNbr;
  77.  
  78.     /* Create an instance and open the Client window */
  79.     lpfnClientProc = MakeProcInstance( ClientProc, hInst);
  80.     nStatus = DialogBox( hInst, "client", hWnd, lpfnClientProc);
  81.     FreeProcInstance( lpfnClientProc);
  82.  
  83.     /* User selected OK */
  84.     if( nStatus == IDOK)
  85.         {
  86.         /* Add the client */
  87.         if( dwStatus = XDbRecordAdd( hDb, "client", &client,
  88.             sizeof( CLIENT)) )
  89.             {
  90.             DbError( hWnd, dwStatus, __FILE__, __LINE__);
  91.             return FALSE;
  92.             }
  93.  
  94.         /* Add the member addresses */
  95.         if( bAddressChange)
  96.             {
  97.             if( ! AddClientAddresses( hWnd))
  98.                 return FALSE;
  99.             }
  100.  
  101.         /* Increment the client number counter and update the setup
  102.            record */
  103.         setup.lNextClientNbr++;
  104.         if( dwStatus = XDbRecordUpdate( hDb, "setup", &setup,
  105.             sizeof( SETUP)))
  106.             {
  107.             DbError( hWnd, dwStatus, __FILE__, __LINE__);
  108.             return FALSE;
  109.             }
  110.  
  111.         /* Flush the database */
  112.         DbFlush( hDb);
  113.  
  114.         /* Add the client to the list box */
  115.         AddToClientListBox( hWnd, &client);
  116.         }
  117.     else    /* IDCANCEL */
  118.         {
  119.         /* Free all address handles for this client */
  120.         if( ! FreeClientAddresses( hWnd))
  121.             return FALSE;
  122.         }
  123.  
  124.     return TRUE;
  125.     }
  126.  
  127.  
  128. /***************************************************************************
  129.  * Function : UpdateClientDlg
  130.  *
  131.  * Purpose  : This function drives the add client dialog box.
  132.  *
  133.  * Returns  : TRUE  - client added
  134.  *            FALSE - update aborted or error in update
  135.  ***************************************************************************/
  136. BOOL UpdateClientDlg( HANDLE hWnd)
  137.     {
  138.     short nStatus, nIndex;
  139.     DWORD dwStatus;
  140.     FARPROC lpfnClientProc;
  141.  
  142.     /* Set the mode */
  143.     eMode = MODE_UPDATE;
  144.  
  145.     /* Initialize the client structure */
  146.     if( ! GetSelectedClient( hWnd, &client, &nIndex))
  147.         {
  148.         MessageBeep( 0);
  149.         return FALSE;
  150.         }
  151.  
  152.     /* Create an instance and open the Client window */
  153.     lpfnClientProc = MakeProcInstance( ClientProc, hInst);
  154.     nStatus = DialogBox( hInst, "client", hWnd, lpfnClientProc);
  155.     FreeProcInstance( lpfnClientProc);
  156.  
  157.     /* User selected OK */
  158.     if( nStatus == IDOK)
  159.         {
  160.         /* Update the client */
  161.         if( dwStatus = XDbRecordUpdate( hDb, "client", &client,
  162.             sizeof( CLIENT)) )
  163.             {
  164.             DbError( hWnd, dwStatus, __FILE__, __LINE__);
  165.             return FALSE;
  166.             }
  167.  
  168.         /* If any address has changed, delete all member addresses and
  169.            add addresses in handle table. */
  170.         if( bAddressChange)
  171.             {
  172.             if( ! DeleteClientAddresses( hWnd))
  173.                 return FALSE;
  174.             if( ! AddClientAddresses( hWnd))
  175.                 return FALSE;
  176.             }
  177.  
  178.         /* Flush the database */
  179.         DbFlush( hDb);
  180.  
  181.         /* Update the listbox */
  182.         DeleteFromClientListBox( hWnd, nIndex);
  183.         AddToClientListBox( hWnd, &client);
  184.         }
  185.     else    /* IDCANCEL */
  186.         {
  187.         /* Free all address handles for this client */
  188.         if( ! FreeClientAddresses( hWnd))
  189.             return FALSE;
  190.         }
  191.  
  192.     return TRUE;
  193.     }
  194.  
  195.  
  196. /***************************************************************************
  197.  * Function : DeleteClientDlg
  198.  *
  199.  * Purpose  : This function drives the client record deletion process.
  200.  *
  201.  * Returns  : TRUE  - client deleted
  202.  *            FALSE - delete aborted or error in delete
  203.  ***************************************************************************/
  204. BOOL DeleteClientDlg( HWND hWnd)
  205.     {
  206.     short nStatus, nIndex;
  207.     DWORD dwStatus;
  208.  
  209.     /* Initialize the client structure */
  210.     if( ! GetSelectedClient( hWnd, &client, &nIndex))
  211.         {
  212.         MessageBeep( 0);
  213.         return FALSE;
  214.         }
  215.  
  216.     /* Ask user if they're sure */
  217.     nStatus = MessageBox( hWnd, "Are you sure?", "Delete Client",
  218.         MB_ICONQUESTION | MB_YESNO);
  219.  
  220.     /* User selected YES */
  221.     if( nStatus == IDYES)
  222.         {
  223.         /* Delete all member addresses */
  224.         if( ! DeleteClientAddresses( hWnd))
  225.             return FALSE;
  226.  
  227.         /* Delete the client */
  228.         if( dwStatus = DbRecordDelete( hDb, "client"))
  229.             {
  230.             DbError( hWnd, dwStatus, __FILE__, __LINE__);
  231.             return FALSE;
  232.             }
  233.  
  234.         /* Flush the database */
  235.         DbFlush( hDb);
  236.  
  237.         /* Remove client from listbox */
  238.         if( ! DeleteFromClientListBox( hWnd, nIndex))
  239.             return FALSE;
  240.         }
  241.  
  242.     return TRUE;
  243.     }
  244.  
  245.  
  246. /***************************************************************************
  247.  * Function : GetSelectedClient
  248.  *
  249.  * Purpose  : This function retrieves the selected client record from
  250.  *            the database.  It uses the client number in the string
  251.  *            of the listbox as a key value to retrieve upon.  The
  252.  *            listbox index is also returned.
  253.  *
  254.  * Returns  : TRUE  - client retrieved
  255.  *            FALSE - error
  256.  ***************************************************************************/
  257. static BOOL GetSelectedClient( HWND hWnd, CLIENT *pClient, short *pIndex)
  258.     {
  259.     LONG lKey;
  260.     DWORD dwStatus;
  261.     char szBuffer[64], *pTemp;
  262.  
  263.     /* Get the listbox selection */
  264.     if( (*pIndex = (short)SendMessage( hWndClientLB, LB_GETCURSEL, 0, 0L))
  265.         == LB_ERR)
  266.         {
  267.         return FALSE;
  268.         }
  269.     if( SendMessage( hWndClientLB, LB_GETTEXT, *pIndex,
  270.         (LONG)(LPSTR)szBuffer) == LB_ERR)
  271.         {
  272.         MessageBox( hWnd, "SendMessage / LB_GETTEXT", "Fatal Error",
  273.             MB_ICONEXCLAMATION | MB_OK);
  274.         return FALSE;
  275.         }
  276.  
  277.     /* Find the client number in the selected string */
  278.     if( bSortByNumber)
  279.         pTemp = strtok( szBuffer, " ");
  280.     else
  281.         {
  282.         pTemp = strchr( szBuffer, 0);
  283.         for( pTemp-- ; *pTemp != ' ' ; pTemp--)
  284.             ;
  285.         pTemp++;
  286.         }
  287.     lKey = atol( pTemp);
  288.  
  289.     /* Retrieve the record */
  290.     if( dwStatus = XDbRecordGetByKey( hDb, "client", "lClientNbr", pClient,
  291.         sizeof( CLIENT), &lKey, sizeof( LONG)))
  292.         {
  293.         DbError( hWnd, dwStatus, __FILE__, __LINE__);
  294.         return FALSE;
  295.         }
  296.  
  297.     return TRUE;
  298.     }
  299.  
  300.  
  301. /***************************************************************************
  302.  * Function : AddClientAddresses
  303.  *
  304.  * Purpose  : This function adds the addresses associated with the client
  305.  *            record.  A base handle, 'hAddresses', contains a null
  306.  *            terminated array of address handles (records) to be added.
  307.  *
  308.  * Returns  : TRUE  - add ok
  309.  *            FALSE - error in add
  310.  ***************************************************************************/
  311. static BOOL AddClientAddresses( HWND hWnd)
  312.     {
  313.     HANDLE FAR *lpAddressHandle;
  314.     DWORD dwStatus;
  315.  
  316.     /* If no addresses, return */
  317.     if( ! hAddresses)
  318.         return TRUE;
  319.  
  320.     /* Get a pointer to the array of address handles */
  321.     if( ! (lpAddressHandle = (HANDLE FAR *)GlobalLock( hAddresses)))
  322.         {
  323.         MessageBox( hWnd, "Memory: GlobalLock", "Fatal Error",
  324.             MB_ICONEXCLAMATION | MB_OK);
  325.         return FALSE;
  326.         }
  327.  
  328.     for( ; *lpAddressHandle ; lpAddressHandle++)
  329.         {
  330.         /* Add the address record */
  331.         if( dwStatus = DbRecordAdd( hDb, "address", *lpAddressHandle))
  332.             {
  333.             DbError( hWnd, dwStatus, __FILE__, __LINE__);
  334.             return FALSE;
  335.             }
  336.         /* Make a set connection between the client and address.  The
  337.            address record will be a member of the client. */
  338.         if( dwStatus = DbSetAdd( hDb, "client", "address"))
  339.             {
  340.             DbError( hWnd, dwStatus, __FILE__, __LINE__);
  341.             return FALSE;
  342.             }
  343.  
  344.         /* Free the address handle */
  345.         if( GlobalFree( *lpAddressHandle))
  346.             {
  347.             MessageBox( hWnd, "Memory: GlobalFree", "Fatal Error",
  348.                 MB_ICONEXCLAMATION | MB_OK);
  349.             return FALSE;
  350.             }
  351.         }
  352.  
  353.     /* Unlock and free the array of address handles */
  354.     GlobalUnlock( hAddresses);
  355.     if( GlobalFree( hAddresses))
  356.         {
  357.         MessageBox( hWnd, "Memory: GlobalFree", "Fatal Error",
  358.             MB_ICONEXCLAMATION | MB_OK);
  359.         return FALSE;
  360.         }
  361.  
  362.     return TRUE;
  363.     }
  364.  
  365.  
  366. /***************************************************************************
  367.  * Function : DeleteClientAddresses
  368.  *
  369.  * Purpose  : This function deletes all address records that are members
  370.  *            of the selected client.
  371.  *
  372.  * Returns  : TRUE  - delete ok
  373.  *            FALSE - error in delete
  374.  ***************************************************************************/
  375. static BOOL DeleteClientAddresses( HWND hWnd)
  376.     {
  377.     DWORD dwStatus;
  378.  
  379.     while( 1)
  380.         {
  381.         /* Set currency to first member record in the set */
  382.         dwStatus = DbSetFindFirst( hDb, "client", "address");
  383.  
  384.         /* Check for errors */
  385.         if( dwStatus == E_NOTFOUND)
  386.             break;
  387.         if( dwStatus)
  388.             {
  389.             DbError( hWnd, dwStatus, __FILE__, __LINE__);
  390.             return FALSE;
  391.             }
  392.  
  393.         /* IMPORTANT: All set connections must be deleted before the member
  394.            record of a set is deleted */
  395.  
  396.         /* Delete the set connection between client (owner) and address
  397.            (member) */
  398.         if( dwStatus = DbSetDelete( hDb, "client", "address"))
  399.             {
  400.             DbError( hWnd, dwStatus, __FILE__, __LINE__);
  401.             return FALSE;
  402.             }
  403.  
  404.         /* Delete the address record */
  405.         if( dwStatus = DbRecordDelete( hDb, "address"))
  406.             {
  407.             DbError( hWnd, dwStatus, __FILE__, __LINE__);
  408.             return FALSE;
  409.             }
  410.         }
  411.  
  412.     return TRUE;
  413.     }
  414.  
  415.  
  416. /***************************************************************************
  417.  * Function : FreeClientAddresses
  418.  *
  419.  * Purpose  : This function frees all addresses associated with the
  420.  *            selected client.  'hAddresses' contains a handle to
  421.  *            a null terminated array of address handles in the listbox.
  422.  *            
  423.  * Returns  : TRUE  - free ok
  424.  *            FALSE - error in free
  425.  ***************************************************************************/
  426. static BOOL FreeClientAddresses( HWND hWnd)
  427.     {
  428.     HANDLE FAR *lpAddressHandle;
  429.  
  430.     /* If no addresses, return */
  431.     if( ! hAddresses)
  432.         return TRUE;
  433.  
  434.     /* Get a pointer to the array of address handles */
  435.     if( ! (lpAddressHandle = (HANDLE FAR *)GlobalLock( hAddresses)))
  436.         {
  437.         MessageBox( hWnd, "Memory: GlobalLock", "Fatal Error",
  438.             MB_ICONEXCLAMATION | MB_OK);
  439.         return FALSE;
  440.         }
  441.  
  442.     for( ; *lpAddressHandle ; lpAddressHandle++)
  443.         {
  444.         /* Free the address handle */
  445.         if( GlobalFree( *lpAddressHandle))
  446.             {
  447.             MessageBox( hWnd, "Memory: GlobalFree", "Fatal Error",
  448.                 MB_ICONEXCLAMATION | MB_OK);
  449.             return FALSE;
  450.             }
  451.         }
  452.  
  453.     /* Unlock and free the array of address handles */
  454.     GlobalUnlock( hAddresses);
  455.     if( GlobalFree( hAddresses))
  456.         {
  457.         MessageBox( hWnd, "Memory: GlobalFree", "Fatal Error",
  458.             MB_ICONEXCLAMATION | MB_OK);
  459.         return FALSE;
  460.         }
  461.  
  462.     return TRUE;
  463.     }
  464.  
  465.  
  466. /***************************************************************************
  467.  * Function : ClientProc
  468.  *
  469.  * Purpose  : This function is the window procedure for 'add' and 'update'
  470.  *            client.
  471.  *
  472.  * Returns  : TRUE  - message processed
  473.  *            FALSE - message not processed
  474.  ***************************************************************************/
  475. BOOL FAR PASCAL ClientProc( HWND hDlg, unsigned iMessage, WORD wParam,
  476.     LONG lParam)
  477.     {
  478.     switch( iMessage)
  479.         {
  480.         case WM_INITDIALOG:
  481.             {
  482.             short nTab;
  483.  
  484.             if( eMode == MODE_ADD)
  485.                 SetWindowText( hDlg, "Add Client");
  486.             else
  487.                 SetWindowText( hDlg, "Update Client");
  488.  
  489.             /* Set a tab stop in the listbox out of sight.  The handle
  490.                to the ADDRESS structure will be stored here (in ASCII) */
  491.             nTab = (LOWORD( GetDialogBaseUnits()) * 100) / 4;
  492.             SendDlgItemMessage( hDlg, IDC_ADDR_LISTBOX, LB_SETTABSTOPS, 1,
  493.                 (LONG)(LPSTR)&nTab);
  494.  
  495.             SetClientFields( hDlg);
  496.  
  497.             /* If update mode, load the address listbox */
  498.             if( eMode == MODE_UPDATE)
  499.                 LoadAddressListBox( hDlg);
  500.  
  501.             /* Set focus to Name field */
  502.             SetFocus( GetDlgItem( hDlg, IDC_EDIT_NAME));
  503.  
  504.             return TRUE;
  505.             }
  506.  
  507.         case WM_COMMAND:
  508.             switch( wParam)
  509.                 {
  510.                 case IDOK:
  511.                     if( ! GetClientFields( hDlg))
  512.                         break;
  513.                     StoreAddressHandles( hDlg);
  514.                     EndDialog( hDlg, IDOK);
  515.                     break;
  516.  
  517.                 case IDCANCEL:
  518.                     StoreAddressHandles( hDlg);
  519.                     EndDialog( hDlg, IDCANCEL);
  520.                     break;
  521.  
  522.                 case IDC_ADD_ADDR:
  523.                     if( AddAddressDlg( hDlg))
  524.                         bAddressChange = TRUE;
  525.                     break;
  526.  
  527.                 case IDC_UPDATE_ADDR:
  528.                     if( UpdateAddressDlg( hDlg))
  529.                         bAddressChange = TRUE;
  530.                     break;
  531.  
  532.                 case IDC_DELETE_ADDR:
  533.                     if( DeleteAddressDlg( hDlg))
  534.                         bAddressChange = TRUE;
  535.                     break;
  536.                 }
  537.             return TRUE;
  538.         }
  539.  
  540.     return FALSE;
  541.     }
  542.  
  543.  
  544. /***************************************************************************
  545.  * Function : SetClientFields
  546.  *
  547.  * Purpose  : This function initializes the client dialog fields.  It
  548.  *            uses the static 'client' structure.
  549.  *
  550.  * Returns  : n/a
  551.  ***************************************************************************/
  552. static void SetClientFields( HWND hDlg)
  553.     {
  554.     char szBuffer[36];
  555.  
  556.     /* Set client number */
  557.     ltoa( client.lClientNbr, szBuffer, 10);
  558.     SetDlgItemText( hDlg, IDC_EDIT_NUMBER , szBuffer);
  559.  
  560.     /* Set client name */
  561.     SendDlgItemMessage( hDlg, IDC_EDIT_NUMBER, EM_LIMITTEXT,
  562.         sizeof( client.szName) - 1, 0L);
  563.     SetDlgItemText( hDlg, IDC_EDIT_NAME, client.szName);
  564.  
  565.     /* Set client description */
  566.     SendDlgItemMessage( hDlg, IDC_EDIT_DESC, EM_LIMITTEXT,
  567.         sizeof( client.szDescription) - 1, 0L);
  568.     SetDlgItemText( hDlg, IDC_EDIT_DESC, client.szDescription);
  569.  
  570.     /* Set client balance */
  571.     sprintf( szBuffer, "%.2f", client.dBalance);
  572.     SendDlgItemMessage( hDlg, IDC_EDIT_BALANCE, EM_LIMITTEXT, 10, 0L);
  573.     SetDlgItemText( hDlg, IDC_EDIT_BALANCE, szBuffer);
  574.     }
  575.  
  576.  
  577. /***************************************************************************
  578.  * Function : GetClientFields
  579.  *
  580.  * Purpose  : This function retrieves the client dialog fields.  It
  581.  *            also performs minor field error checking.  Client fields
  582.  *            are stored in the static 'client' structure.
  583.  *
  584.  * Returns  : TRUE  - fields ok
  585.  *            FALSE - error in field (focus is set)
  586.  ***************************************************************************/
  587. static BOOL GetClientFields( HWND hDlg)
  588.     {
  589.     char szBuffer[36];
  590.  
  591.     GetDlgItemText( hDlg, IDC_EDIT_NAME, client.szName,
  592.         sizeof( client.szName));
  593.     GetDlgItemText( hDlg, IDC_EDIT_DESC, client.szDescription,
  594.         sizeof( client.szDescription));
  595.     GetDlgItemText( hDlg, IDC_EDIT_BALANCE, szBuffer, sizeof( szBuffer));
  596.     client.dBalance = atof( szBuffer);
  597.  
  598.     /* Name field required */
  599.     if( ! *client.szName)
  600.         {
  601.         MessageBox( hDlg, "Name required", "Invalid Input",
  602.             MB_APPLMODAL | MB_ICONEXCLAMATION | MB_OK);
  603.         /* Set focus to Name field */
  604.         SetFocus( GetDlgItem( hDlg, IDC_EDIT_NAME));
  605.  
  606.         return FALSE;
  607.         }
  608.  
  609.     return TRUE;
  610.     }
  611.  
  612.  
  613. /***************************************************************************
  614.  * Function : StoreAddressFields
  615.  *
  616.  * Purpose  : This function stores all the address handles associated
  617.  *            with a client in a null-terminated array.  'hAddresses'
  618.  *            contains a handle to the array.
  619.  *
  620.  * Returns  : TRUE  - address handles stored
  621.  *            FALSE - error
  622.  ***************************************************************************/
  623. static BOOL StoreAddressHandles( HWND hDlg)
  624.     {
  625.     short nCount, i;
  626.     HANDLE FAR *lpAddressHandle;
  627.  
  628.     /* Set handle to address handle array to NULL */
  629.     hAddresses = NULL;
  630.  
  631.     /* Get the number of addresses in listbox */
  632.     nCount = (short)SendMessage( GetDlgItem( hDlg, IDC_ADDR_LISTBOX), LB_GETCOUNT,
  633.         0, 0L);
  634.     if( ! nCount)
  635.         return TRUE;
  636.  
  637.     /* Allocate space to store address handles.  Leave last entry NULL
  638.        to trigger end of addresses. */
  639.     if( ! (hAddresses = GlobalAlloc( GMEM_MOVEABLE | GMEM_ZEROINIT,
  640.         (DWORD)(sizeof( HANDLE) * (nCount + 1)))) )
  641.         {
  642.         MessageBox( hDlg, "Out of memory", "Fatal Error",
  643.             MB_ICONEXCLAMATION | MB_OK);
  644.         return FALSE;
  645.         }
  646.  
  647.     /* Lock handle */
  648.     if( ! (lpAddressHandle = (HANDLE FAR *)GlobalLock( hAddresses)) )
  649.         {
  650.         MessageBox( hDlg, "Memory: GlobalLock", "Fatal Error",
  651.             MB_ICONEXCLAMATION | MB_OK);
  652.         return FALSE;
  653.         }
  654.  
  655.     /* Get each address handle and store it */
  656.     for( i=0 ; i<nCount ; i++)
  657.         {
  658.         *lpAddressHandle = GetAddressHandle( hDlg, i);
  659.         if( ! *lpAddressHandle)
  660.             return FALSE;
  661.         lpAddressHandle++;
  662.         }
  663.  
  664.     /* Unlock handle */
  665.     GlobalUnlock( hAddresses);
  666.  
  667.     return TRUE;
  668.     }
  669.